#!/bin/bash

# 串口测试工具 - 用于测试串口可用性

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# 默认设置
DEFAULT_BAUDRATE=9600
DEFAULT_TIMEOUT=5
TEST_MESSAGE="Serial Port Test Message"

# 帮助信息
show_help() {
    cat << EOF
用法: $0 [选项] <串口设备>

选项:
    -p, --port PORT        指定串口设备 (必需，如: /dev/ttyUSB0, /dev/ttyS0)
    -b, --baudrate RATE    设置波特率 (默认: ${DEFAULT_BAUDRATE})
                          常用值: 9600, 19200, 38400, 57600, 115200
    -m, --message MSG      设置测试消息 (默认: "Serial Port Test Message")
    -c, --count NUM        发送次数 (默认: 1, 0表示持续发送)
    -t, --timeout SEC      等待响应超时时间 (默认: ${DEFAULT_TIMEOUT}秒)
    -f, --file FILE        从文件读取消息内容
    -h, --hex              以十六进制格式发送消息
    -r, --receive          同时监听串口响应
    -l, --loopback         回环测试模式 (需要连接TX和RX引脚)
    -v, --verbose          详细输出模式
    --help                 显示此帮助信息

示例:
    $0 -p /dev/ttyUSB0                          # 基本测试
    $0 -p /dev/ttyUSB0 -b 115200                # 指定115200波特率
    $0 -p /dev/ttyUSB0 -m "Hello" -c 5          # 发送5次"Hello"
    $0 -p /dev/ttyUSB0 -r -t 10                 # 监听10秒响应
    $0 -p /dev/ttyUSB0 -l -v                    # 回环测试(详细模式)
    $0 -p /dev/ttyUSB0 -f test.txt              # 从文件发送内容
    $0 -p /dev/ttyUSB0 -h -m "A0B1C2"           # 发送十六进制数据

回环测试说明:
    在回环测试模式下，需要将串口的TX和RX引脚用杜邦线短接
    这样发送的数据会立即被接收，用于验证串口基本功能

EOF
}

# 检查命令是否存在
check_command() {
    if ! command -v "$1" &> /dev/null; then
        echo -e "${RED}错误: 未找到 $1 命令，请先安装${NC}"
        return 1
    fi
    return 0
}

# 检查串口设备是否存在
check_serial_port() {
    local port=$1
    
    if [ ! -e "$port" ]; then
        echo -e "${RED}错误: 串口设备 $port 不存在${NC}"
        return 1
    fi
    
    if [ ! -c "$port" ]; then
        echo -e "${RED}错误: $port 不是字符设备${NC}"
        return 1
    fi
    
    # 检查设备权限
    if [ ! -w "$port" ]; then
        echo -e "${RED}错误: 没有写入权限访问 $port${NC}"
        echo -e "${YELLOW}请尝试使用sudo或以有权限的用户运行${NC}"
        return 1
    fi
    
    return 0
}

# 列出可用串口
list_serial_ports() {
    echo -e "${BLUE}=== 可用的串口设备 ===${NC}"
    
    local found=0
    
    for pattern in "/dev/ttyS*" "/dev/ttyUSB*" "/dev/ttyACM*"; do
        for port in $pattern; do
            if [ -c "$port" ]; then
                local size=$(stat -c "%s" "$port" 2>/dev/null || echo "N/A")
                if [ "$size" != "0" ] && [ "$size" != "N/A" ]; then
                    echo -e "串口: ${GREEN}$port${NC}"
                    found=1
                fi
            fi
        done
    done
    
    if [ $found -eq 0 ]; then
        echo -e "${YELLOW}未找到任何可用的串口设备${NC}"
    fi
}

# 安装必要工具
install_tools() {
    echo -e "${YELLOW}检测到缺少必要工具，正在安装...${NC}"
    
    if command -v apt-get &> /dev/null; then
        sudo apt-get update
        sudo apt-get install -y minicom screen python3-serial
    elif command -v yum &> /dev/null; then
        sudo yum install -y minicom screen python3-serial
    elif command -v dnf &> /dev/null; then
        sudo dnf install -y minicom screen python3-serial
    else
        echo -e "${RED}错误: 无法自动安装工具，请手动安装:${NC}"
        echo -e "Ubuntu/Debian: sudo apt-get install minicom screen python3-serial"
        echo -e "CentOS/RHEL: sudo yum install minicom screen python3-serial"
        return 1
    fi
}

# 检查必要工具
check_tools() {
    local tools=("stty" "echo")
    local missing=()
    
    for tool in "${tools[@]}"; do
        if ! check_command "$tool"; then
            missing+=("$tool")
        fi
    done
    
    if [ ${#missing[@]} -gt 0 ]; then
        echo -e "${RED}错误: 缺少必要工具: ${missing[*]}${NC}"
        return 1
    fi
    
    # 可选工具
    if ! check_command minicom; then
        echo -e "${YELLOW}警告: 未找到minicom，某些功能可能受限${NC}"
        echo -e "${YELLOW}建议安装: sudo apt-get install minicom${NC}"
    fi
    
    return 0
}

# 设置串口参数
setup_serial_port() {
    local port=$1
    local baudrate=$2
    
    echo -e "${BLUE}设置串口参数: $port @ ${baudrate}bps${NC}"
    
    # 保存原始设置
    local original_settings=$(stty -g -F "$port" 2>/dev/null)
    
    # 设置串口参数
    stty -F "$port" "$baudrate" cs8 -cstopb -parenb -icanon min 1 time 1 &>/dev/null
    
    if [ $? -ne 0 ]; then
        echo -e "${RED}错误: 无法设置串口参数${NC}"
        return 1
    fi
    
    echo "$original_settings"
    return 0
}

# 恢复串口设置
restore_serial_settings() {
    local port=$1
    local settings=$2
    
    if [ -n "$settings" ]; then
        stty -F "$port" "$settings" &>/dev/null
        echo -e "${GREEN}串口设置已恢复${NC}"
    fi
}

# 发送消息到串口
send_serial_message() {
    local port=$1
    local message=$2
    local hex_mode=$3
    local verbose=$4
    
    if [ "$hex_mode" = "true" ]; then
        # 十六进制模式发送
        echo -e "${CYAN}发送十六进制: $message${NC}"
        echo "$message" | xxd -r -p > "$port"
    else
        # 文本模式发送
        if [ "$verbose" = "true" ]; then
            echo -e "${CYAN}发送消息: $message${NC}"
        fi
        echo -e "$message" > "$port"
    fi
    
    if [ $? -eq 0 ]; then
        if [ "$verbose" = "true" ]; then
            echo -e "${GREEN}消息发送成功${NC}"
        fi
        return 0
    else
        echo -e "${RED}消息发送失败${NC}"
        return 1
    fi
}

# 从文件发送内容
send_file_content() {
    local port=$1
    local file=$2
    local hex_mode=$3
    local verbose=$4
    
    if [ ! -f "$file" ]; then
        echo -e "${RED}错误: 文件 $file 不存在${NC}"
        return 1
    fi
    
    if [ "$hex_mode" = "true" ]; then
        echo -e "${CYAN}从文件发送十六进制内容: $file${NC}"
        xxd -r -p "$file" > "$port"
    else
        echo -e "${CYAN}从文件发送内容: $file${NC}"
        cat "$file" > "$port"
    fi
    
    if [ $? -eq 0 ]; then
        echo -e "${GREEN}文件内容发送成功${NC}"
        return 0
    else
        echo -e "${RED}文件内容发送失败${NC}"
        return 1
    fi
}

# 接收串口数据
receive_serial_data() {
    local port=$1
    local timeout=$2
    local hex_mode=$3
    local verbose=$4
    
    echo -e "${CYAN}监听串口响应 (超时: ${timeout}秒)...${NC}"
    
    # 设置超时
    local end_time=$((SECONDS + timeout))
    
    while [ $SECONDS -lt $end_time ]; do
        if read -r -t 1 line < "$port"; then
            if [ "$hex_mode" = "true" ]; then
                echo -e "${GREEN}接收十六进制: $(echo -n "$line" | xxd -p)${NC}"
            else
                echo -e "${GREEN}接收: $line${NC}"
            fi
        fi
    done
    
    echo -e "${YELLOW}监听超时${NC}"
}

# 回环测试
loopback_test() {
    local port=$1
    local baudrate=$2
    local message=$3
    local verbose=$4
    
    echo -e "${BLUE}=== 回环测试模式 ===${NC}"
    echo -e "${YELLOW}请确保已将 $port 的TX和RX引脚短接${NC}"
    echo -e "测试消息: $message"
    echo -e "波特率: $baudrate"
    echo ""
    
    # 发送测试消息
    echo -e "$message" > "$port"
    
    # 接收响应
    echo -e "${CYAN}等待接收回环数据...${NC}"
    
    local received=""
    local end_time=$((SECONDS + 3))
    
    while [ $SECONDS -lt $end_time ]; do
        if read -r -t 1 line < "$port"; then
            received="$line"
            break
        fi
    done
    
    if [ -n "$received" ]; then
        if [ "$received" = "$message" ]; then
            echo -e "${GREEN}✓ 回环测试成功!${NC}"
            echo -e "发送: $message"
            echo -e "接收: $received"
            return 0
        else
            echo -e "${RED}✗ 回环测试失败${NC}"
            echo -e "发送: $message"
            echo -e "接收: $received"
            return 1
        fi
    else
        echo -e "${RED}✗ 未接收到回环数据${NC}"
        echo -e "${YELLOW}请检查:${NC}"
        echo -e "1. TX和RX引脚是否正确短接"
        echo -e "2. 串口设备是否正确"
        echo -e "3. 波特率设置是否匹配"
        return 1
    fi
}

# 主测试函数
serial_port_test() {
    local port="$1"
    local baudrate="$2"
    local message="$3"
    local count="$4"
    local timeout="$5"
    local hex_mode="$6"
    local receive_mode="$7"
    local loopback_mode="$8"
    local verbose="$9"
    local file_input="${10}"
    
    # 检查串口设备
    if ! check_serial_port "$port"; then
        return 1
    fi
    
    # 检查工具
    if ! check_tools; then
        read -p "是否尝试安装必要工具? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            install_tools
        else
            return 1
        fi
    fi
    
    # 设置串口参数
    local original_settings
    original_settings=$(setup_serial_port "$port" "$baudrate")
    if [ $? -ne 0 ]; then
        return 1
    fi
    
    # 确保退出时恢复设置
    trap 'restore_serial_settings "$port" "$original_settings"' EXIT
    
    echo -e "${BLUE}=== 串口测试开始 ===${NC}"
    echo -e "设备: $port"
    echo -e "波特率: ${baudrate}bps"
    echo -e "测试模式: $( [ "$loopback_mode" = "true" ] && echo "回环测试" || echo "普通测试" )"
    echo ""
    
    # 回环测试模式
    if [ "$loopback_mode" = "true" ]; then
        loopback_test "$port" "$baudrate" "$message" "$verbose"
        return $?
    fi
    
    # 文件输入模式
    if [ -n "$file_input" ]; then
        send_file_content "$port" "$file_input" "$hex_mode" "$verbose"
    else
        # 普通发送模式
        local current_count=0
        while [ "$count" -eq 0 ] || [ "$current_count" -lt "$count" ]; do
            current_count=$((current_count + 1))
            
            if [ "$count" -gt 1 ] || [ "$count" -eq 0 ]; then
                echo -e "${CYAN}--- 第 ${current_count} 次发送 ---${NC}"
            fi
            
            if ! send_serial_message "$port" "$message" "$hex_mode" "$verbose"; then
                return 1
            fi
            
            # 如果需要接收响应
            if [ "$receive_mode" = "true" ]; then
                receive_serial_data "$port" "$timeout" "$hex_mode" "$verbose"
            fi
            
            # 如果不是持续发送，等待一下
            if [ "$count" -ne 0 ] && [ "$current_count" -lt "$count" ]; then
                sleep 1
            elif [ "$count" -eq 0 ]; then
                sleep 2
            fi
        done
    fi
    
    echo -e "${GREEN}=== 串口测试完成 ===${NC}"
    return 0
}

# 参数解析
main() {
    local port=""
    local baudrate="$DEFAULT_BAUDRATE"
    local message="$TEST_MESSAGE"
    local count=1
    local timeout="$DEFAULT_TIMEOUT"
    local hex_mode=false
    local receive_mode=false
    local loopback_mode=false
    local verbose=false
    local file_input=""
    
    # 如果没有参数，显示帮助
    if [ $# -eq 0 ]; then
        show_help
        list_serial_ports
        exit 0
    fi
    
    # 参数解析
    while [[ $# -gt 0 ]]; do
        case $1 in
            -p|--port)
                port="$2"
                shift 2
                ;;
            -b|--baudrate)
                baudrate="$2"
                shift 2
                ;;
            -m|--message)
                message="$2"
                shift 2
                ;;
            -c|--count)
                count="$2"
                shift 2
                ;;
            -t|--timeout)
                timeout="$2"
                shift 2
                ;;
            -f|--file)
                file_input="$2"
                shift 2
                ;;
            -h|--hex)
                hex_mode=true
                shift
                ;;
            -r|--receive)
                receive_mode=true
                shift
                ;;
            -l|--loopback)
                loopback_mode=true
                shift
                ;;
            -v|--verbose)
                verbose=true
                shift
                ;;
            --help)
                show_help
                exit 0
                ;;
            *)
                # 如果没有选项前缀，认为是端口参数
                if [[ "$1" =~ ^/dev/ ]]; then
                    port="$1"
                else
                    echo -e "${RED}错误: 未知参数 $1${NC}"
                    show_help
                    exit 1
                fi
                shift
                ;;
        esac
    done
    
    # 检查必需参数
    if [ -z "$port" ]; then
        echo -e "${RED}错误: 必须指定串口设备${NC}"
        show_help
        list_serial_ports
        exit 1
    fi
    
    # 运行测试
    serial_port_test "$port" "$baudrate" "$message" "$count" "$timeout" "$hex_mode" "$receive_mode" "$loopback_mode" "$verbose" "$file_input"
}

# 脚本入口
main "$@"